package org.framework.lazy.cloud.network.heartbeat.protocol.handler;


import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.NettyByteBuf;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import org.framework.lazy.cloud.network.heartbeat.protocol.log.ProxyLog;
import org.framework.lazy.cloud.network.heartbeat.protocol.properties.ProtocolProxyProperties;
import org.framework.wu.framework.queue.Message;
import org.framework.wu.framework.queue.MessageQueue;
import org.framework.wu.framework.queue.MessageQueueFactory;
import org.wu.framework.spring.utils.SpringContextHolder;

import java.nio.charset.StandardCharsets;
import java.util.UUID;


/**
 * 代理，真实通道发送数据
 */
@Slf4j
public class NettyProxy2RealInboundHandler extends SimpleChannelInboundHandler<NettyByteBuf> {


    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, NettyByteBuf nettyByteBuf) throws Exception {
        log.debug("本地转发客户端的请求到代理服务器");
        Channel currentChannel = ctx.channel();
        byte[] data = nettyByteBuf.getData();
        // 把数据转到真实服务
        ByteBuf buf = currentChannel.config().getAllocator().buffer(data.length);
        buf.writeBytes(data);

        log.info("发送请求到真实客户端：{}", new String(data, StandardCharsets.UTF_8));

        Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(currentChannel);
        String targetIp = ChannelAttributeKeyUtils.getTargetIp(nextChannel);
        Integer targetPort = ChannelAttributeKeyUtils.getTargetPort(nextChannel);


        // 设置请求ID
        String requestId = UUID.randomUUID().toString();
        ChannelAttributeKeyUtils.buildRequestId(currentChannel, requestId);
        ChannelAttributeKeyUtils.buildRequestId(nextChannel, requestId);

        if (nextChannel.isActive()) {
            ProtocolProxyProperties protocolProxyProperties = SpringContextHolder.getBean(ProtocolProxyProperties.class);
            if (protocolProxyProperties.getEnableProxyLog()) {
                log.debug("记录代理发送日志开始");
                try {
                    String visitorId = ChannelAttributeKeyUtils.getVisitorId(nextChannel);
                    String sendMsgQueue = protocolProxyProperties.getSendMsgQueue();
                    MessageQueue queue = MessageQueueFactory.getQueue(sendMsgQueue);
                    Message message = new Message();
                    message.setTopic(sendMsgQueue);
                    message.setId(visitorId);
                    ProxyLog proxyLog = new ProxyLog();
                    proxyLog.setVisitorId(visitorId);
                    proxyLog.setHost(targetIp);
                    proxyLog.setPort(targetPort);
                    proxyLog.setSend(data);
                    proxyLog.setRequestId(requestId);
                    message.setBody(proxyLog);
                    queue.send(message);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
                log.debug("记录代理发送日志结束");
            }

            nextChannel.writeAndFlush(buf);
        } else {
            log.info("释放内存");
            ReferenceCountUtil.release(buf);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.debug("客户端与代理服务器的连接已经断开，即将断开代理服务器和目标服务器的连接");
        Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(ctx.channel());
        if (nextChannel.isActive()) {
            if (ctx.channel().isActive()) {
                ctx.channel().writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("NettyProxy2RealInboundHandler exception", cause);
        ctx.close();
    }
}
